package com.reebake.ideal.protect.filter;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.reebake.ideal.cache.util.CacheUtil;
import com.reebake.ideal.crypto.core.SecretKeyService;
import com.reebake.ideal.crypto.entity.SecretKeyEntity;
import com.reebake.ideal.protect.core.AbstractProtectFilter;
import com.reebake.ideal.protect.core.ProtectParameter;
import com.reebake.ideal.protect.core.SignatureService;
import com.reebake.ideal.protect.exception.DuplicateRequestException;
import com.reebake.ideal.protect.exception.SignatureTimeoutException;
import com.reebake.ideal.protect.exception.SignatureVerifyFailureException;
import com.reebake.ideal.protect.properties.ProtectProperties;
import com.reebake.ideal.servlet.core.ByteContentCachingRequestWrapper;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

/**
 * 签名验证过滤器
 */
public class SignatureProtectFilter extends AbstractProtectFilter {
    private final SignatureService signatureService;
    private final SecretKeyService secretKeyService;
    private final static long DUPLICATE_REQUEST_CACHE_TIMEOUT = 8 * 60 * 60 * 1000;

    private String generateDuplicateRequestCacheKey(String key){
        String CACHE_PREFIX_DUPLICATE_REQUEST_PREVENTION = "duplicate_request_prevention:";
        return CACHE_PREFIX_DUPLICATE_REQUEST_PREVENTION + key;
    }

    public SignatureProtectFilter(ProtectProperties protectProperties,
                                  SignatureService signatureService, SecretKeyService secretKeyService) {
        super(protectProperties);
        this.signatureService = signatureService;
        this.secretKeyService = secretKeyService;
    }

    @Override
    protected void protect(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        ByteContentCachingRequestWrapper requestWrapper = new ByteContentCachingRequestWrapper(request);
        ProtectParameter protectParameter = getProtectParameter(request);
        // 校验时间戳
        if(getProtectProperties().getMaxTimeGap() > 0) {
            if(Math.abs(System.currentTimeMillis() - protectParameter.getTimestamp()) > getProtectProperties().getMaxTimeGap()) {
                throw new SignatureTimeoutException();
            }
        }
        // 构建签名内容
        StringBuilder dataBuilder = new StringBuilder();
        dataBuilder.append(protectParameter.getParams());
        String requestURI = request.getRequestURI();
        String queryString = request.getQueryString();
        String path = requestURI;
        if(StrUtil.isNotBlank(queryString)) {
            path = path + "?" + queryString;
        }
        dataBuilder.append(path);
        dataBuilder.append(IoUtil.readUtf8(requestWrapper.getInputStream()));
        String signatureBody = dataBuilder.toString();
        String duplicateRequestKey = SecureUtil.md5(signatureBody);
        Object duplicateValue = CacheUtil.get(generateDuplicateRequestCacheKey(duplicateRequestKey));
        if(ObjUtil.isNull(duplicateValue)) {
            CacheUtil.put(duplicateRequestKey, 0, DUPLICATE_REQUEST_CACHE_TIMEOUT);
        }else {
            throw new DuplicateRequestException();
        }
        SecretKeyEntity secretKeyEntity = secretKeyService.queryByAppKey(protectParameter.getAppKey());

        boolean verifyResult = signatureService.verify(signatureBody, protectParameter, secretKeyEntity);
        if(!verifyResult) {
            throw new SignatureVerifyFailureException();
        }
        filterChain.doFilter(requestWrapper, response);
    }

}
